
import re
import sys
import time
import threading

import lookup
import correct
import pywordnet as wntools;
from templates import *;

XMPPPY_PATH = '../../../misc'
if XMPPPY_PATH not in sys.path:
	sys.path.append(XMPPY_PATH)
import xmpppy as xmpp

# Userbase code
ROSTER_PATH = '../../userbase/input'


class DictBot(threading.Thread):

	def __init__(self, accInfo):
		threading.Thread.__init__(self) 
		self.client = xmpp.Client(accInfo['domain'], debug=0)
		self.client.connect(server = (accInfo['url'], accInfo['port']))
		self.client.auth(accInfo['username'], accInfo['password'])
		self.client.sendInitPresence()
		self.roster = self.client.getRoster()
		self.client.RegisterHandler('message', self.handle_Message)
		self.client.RegisterHandler('presence', self.handle_Presence)
		print 'XMPP: connected %s@%s to %s:%s' % (accInfo['username'], accInfo['domain'], accInfo['url'], accInfo['port'])
		
		# Userbase code
		self.rf = open('%s/%s_roster_%s.txt' % (ROSTER_PATH, accInfo['username'], time.strftime('%Y%m%d%H%M'),), 'at')
		for email in self.roster.getItems():
			self.rf.write('%s\n' % email)
		self.rf.flush()

		self.admins = accInfo['admin'].split(';')
		self.JID = '%s@%s' % (accInfo['username'], accInfo['domain'])
		self.userCount = len(self.roster.getItems())
		self.sleepTime = 0.0
		self.loadCount = 0
		self.loadRate = 0.0
		self.queryCount = 0
		self.queryRate = 0.0
		self.exist = True
		self.inputRegex = re.compile('[^A-Za-z\/]+', re.U)

		if accInfo['accept'] == '1':
			self.acceptUsers = True
		else:
			self.acceptUsers = False

		# Check and set status and autostatus
		self.STATUS_MSG = accInfo['status']
		if len(self.STATUS_MSG) == 0:
			self.autoStatus = True
			self.set_StatusMessage(AUTO_STATUS_MSG % (self.userCount))
		else:
			self.autoStatus = False
			self.set_StatusMessage(self.STATUS_MSG)

	
	def run(self):
		while self.exist:
			self.client.Process()
			time.sleep(2)
	

	def handle_Presence(self, dispatcher, presence):
		"Accepts user subscriptions or refer it to the other bots"
		senderJID = (presence.getFrom()).getStripped()
		self.loadCount += 1

		type = presence.getType()
		if type == 'subscribe':
			print 'ROSTER: request from %s' % (senderJID)
			self.loadCount += 5

			# Userbase code
			self.rf.write('%s\n' % senderJID)
			self.rf.flush()

			if self.acceptUsers:
				self.roster.Authorize(senderJID)
				print 'ROSTER: authorized to %s' % (senderJID)
				return
			else:
				dispatcher.send(xmpp.Message(senderJID, REPLY_PREFIX + SUBSCRIBE_DENIED_MSG))
				self.roster.Unauthorize(senderJID)
				self.roster.Unsubscribe(senderJID)
				self.roster.delItem(senderJID)      # Also delete in the roster
				print 'ROSTER: denied %s' % (senderJID)
				
		elif type == 'subscribed':
			print 'ROSTER: subscribed by %s' % (senderJID)
			dispatcher.send(xmpp.Message(senderJID, REPLY_PREFIX + WELCOME_MSG))
			self.userCount += 1
			self.loadCount += 5
			
			# Update autostatus message
			if self.autoStatus:
				self.set_StatusMessage(AUTO_STATUS_MSG % (self.userCount))


	def handle_Message(self, dispatcher, message):
		"Processes commands and dictionary lookup requests"
		self.loadCount += 3
		request = message.getBody()
		if request:
			self.queryCount += 1
			sender = message.getFrom()
			senderJID = sender.getStripped()
			request = request.strip()

			try: # If the request is non-ascii, printing to the terminal will raise exception
				print '\t%s: <%s> %s' % (self.JID, senderJID, request)
			except UnicodeEncodeError:
				print 'UnicodeEncodeError'

			if request.startswith('/'):
				if len(request) > 1:
					self.process_Command(dispatcher, sender, senderJID, request)

			elif request.startswith(REPLY_PREFIX):
				return

			else:
				# Eliminate unsual inputs that can crash the program
				request = self.inputRegex.sub('', request)
				if len(request) > 0 and len(request) < 20:
					self.process_Query(dispatcher, sender, senderJID, request)


	def process_Command(self, dispatcher, sender, senderJID, command):
		command = command[1:].split()
		cmd = command[0]

# USER COMMANDS SECTION
		if cmd == 'help':
			reply = USER_HELP_MSG

		elif cmd == 'about':
			reply = ABOUT_MSG

		elif cmd == 'stats':
			reply = STATS_MSG % (self.userCount, lookup.mainDictSize, self.queryRate, self.loadRate)
			if self.acceptUsers:
				reply += ACCEPT_MSG % 'Accepting'
			else:
				reply += ACCEPT_MSG % 'Not accepting'

		# If the sender is not in the admin list, don't check any further for admin commands
		elif senderJID not in self.admins:
			reply = USER_HELP_MSG

# ADMIN COMMANDS SECTION
		elif cmd == 'status':
			if len(command) > 1:
				self.autoStatus = False
				self.set_StatusMessage(' '.join(command[1:]))
				return
			reply = STATUS_USAGE_MSG

		elif cmd == 'autostatus':
			self.autoStatus = not self.autoStatus
			if self.autoStatus:
				self.set_StatusMessage(AUTO_STATUS_MSG % (self.userCount))
			else:
				self.set_StatusMessage(self.STATUS_MSG)
			return

		elif cmd == 'accept':
			self.acceptUsers = not self.acceptUsers
			if self.acceptUsers:
				reply =	ACCEPT_MSG % 'Accepting'
			else:
				reply = ACCEPT_MSG % 'Not accepting'

		elif cmd == 'exit':
			self.exist = False
			self.rf.close()
			return

		else:
			reply = ADMIN_HELP_MSG
		dispatcher.send(xmpp.Message(sender, REPLY_PREFIX + reply))

		
	def set_StatusMessage(self, newStatusMsg):
		presence = xmpp.protocol.Presence()
		self.STATUS_MSG = newStatusMsg
		presence.setStatus(self.STATUS_MSG)
		self.client.send(presence)


	def process_Query(self, dispatcher, sender, senderJID, query):
		try:
			query = str(query).lower()
		except UnicodeEncodeError:
			return
		#TODO: more preprocessing
		query = query.split()[0]	# only process the first token
		reply = self.get_Meaning(query)
		if reply is None: # if word is not in dictionary, perform morph
			correction = wntools.morphy(query)
			if correction is not None:
				if self.get_Meaning(correction) is not None:
					reply = AUTO_CORRECT_MSG % (correction, self.get_Meaning(correction))
				else:
					reply = None
		if reply is None:   # if word is not in dictionary, auto correct
			correction = correct.correct(query)
			reply = AUTO_CORRECT_MSG % (correction, self.get_Meaning(correction))
		dispatcher.send(xmpp.Message(sender, REPLY_PREFIX + reply))


	def get_Meaning(self, query):
		if lookup.mainDict.has_key(query):
			return lookup.mainDict[query]
		else:
			wordnetResult = lookup.search_WordNet(query)
			if wordnetResult:
				return WORD_IN_WORDNET_MSG % (wordnetResult)
		return None
	

	def is_ValidJID(self, JID):
		return re.match('\S+@\S+\.\w+', JID)
